#include "dominion.h"
#include "dominion_helpers.h"
#include "sput/sput.h"
#include <malloc.h>
#include <stdlib.h>
#include <time.h>

struct gameState *testState;

static void test_alloc_game(void)
{
    size_t gameStateSize = sizeof(struct gameState);
    size_t actualSize;
    testState = newGame();
    actualSize = malloc_usable_size(testState);
    printf("Needed size: %zu; Actual size: %zu\n", gameStateSize, actualSize);
    sput_fail_if(testState == NULL, "no memory allocated for gameState");
    sput_fail_unless(gameStateSize == actualSize, "wrong amount of memory allocated for gameState");
}

static void test_init_game(void)
{
    int kc_novic_count = 8;
    int kc_notpres_count = 7;
    int i, j;
    int copper_count;
    int estate_count;
    int curse_count;
    int victory_count;
    int numPlayers;
    int supplyPos;

    int kc[10] = {adventurer, council_room, feast, gardens, mine, remodel,
                  smithy, village, baron, great_hall
                 };

    int kc_novic[8] = {adventurer, council_room, feast, mine, remodel,
                       smithy, village, baron
                      };

    int kc_nodiff[10] = {adventurer, adventurer, adventurer, adventurer,
                         adventurer, adventurer, adventurer, adventurer, adventurer
                        };

    int kc_notpres[7] = {minion, steward, tribute,  ambassador,  embargo,  salvager,  treasure_map};

    // Test player count checking
    sput_fail_unless(initializeGame(MAX_PLAYERS + 1, kc, rand(), testState) == -1, "return -1 when num_players > MAX_PLAYERS");
    sput_fail_unless(initializeGame(0, kc, rand(), testState) == -1, "return -1 when num_players < 2");

    // Test card uniqueness checking
    sput_fail_unless(initializeGame(2, kc_nodiff, rand(), testState) == -1, "return -1 when kingdom cards unique");

    for(numPlayers = 2; numPlayers <= MAX_PLAYERS; numPlayers++) {
        // iterate though all cards
        for(supplyPos = curse; supplyPos <= treasure_map; supplyPos++) {
            if(numPlayers == 2) {
                curse_count = 10;
                victory_count = 8;
            } else {
                if(numPlayers == 3) {
                    curse_count = 20;
                } else {
                    curse_count = 30;
                }
                victory_count = 12;
            }

            // Test card initialization
            initializeGame(numPlayers, kc, rand(), testState);
            sput_fail_unless(testState->supplyCount[curse] == curse_count, "set curse card count to 10 for 2 players");
            sput_fail_unless(testState->supplyCount[estate] == victory_count, "set estate count to 8 for 2 players");
            sput_fail_unless(testState->supplyCount[duchy] == victory_count, "set duchy count to 8 for 2 players");
            sput_fail_unless(testState->supplyCount[province] == victory_count, "set province count to 8 for 2 players");
            sput_fail_unless(testState->supplyCount[great_hall] == victory_count, "set great_hall count to 8 for 2 players");
            sput_fail_unless(testState->supplyCount[gardens] == victory_count, "set gardens count to 8 for 2 players");

            // Test treasure card initialization
            sput_fail_unless(testState->supplyCount[copper] == (60 - (7 * numPlayers)), "copper count incorrect");
            sput_fail_unless(testState->supplyCount[silver] == 40, "silver count set to 40");
            sput_fail_unless(testState->supplyCount[gold] == 30, "gold count set to 30");

            // Test non-victory card initialization
            for(i = 0; i < kc_novic_count; i++) {
                sput_fail_unless(testState->supplyCount[kc_novic[i]] == 10, "set non-victory card count to 10");
            }

            for(i = 0; i < kc_notpres_count; i++) {
                sput_fail_unless(testState->supplyCount[kc_notpres[i]] == -1, "set non-present card count to -1");
            }

            // Test copper and estate initialization
            for(i = 0; i < 3; i++) {
                copper_count = estate_count = 0;
                for(j = 0; j < 10; j++) {
                    if(testState->deck[i][j] == estate) {
                        estate_count++;
                    }
                    if(testState->deck[i][j] == copper) {
                        copper_count++;
                    }
                }
                sput_fail_unless(estate_count == 3, "put 3 estates in hand");
                sput_fail_unless(copper_count == 7, "put 7 coppers in hand");
            }

            // Test hand count and discard initialization
            for(i = 0; i < 3; i++) {
                sput_fail_unless(testState->handCount[i] == 0, "hand count set to 0");
                sput_fail_unless(testState->discardCount[i] == 0, "discard count set to 0");
            }

            // Check embargo token initialization
            for(i = curse; i <= treasure_map; i++) {
                sput_fail_unless(testState->embargoTokens[i] == 0, "embargo tokens initialized to 0");
            }

            // Check play state initialization
            sput_fail_unless(testState->outpostPlayed == 0, "outposts played set to 0");
            sput_fail_unless(testState->phase == 0, "phase set to 0");
            sput_fail_unless(testState->numActions == 1, "number of actions set to 0");
            sput_fail_unless(testState->numBuys == 1, "number of buys set to 0");
            sput_fail_unless(testState->playedCardCount == 0, "played card count set to 0");
            sput_fail_unless(testState->whoseTurn == 0, "whose turn set to 0");
            sput_fail_unless(testState->handCount[0] == 0, "hand count set to 0");
        }
    }
}

int main(int argc, char *argv[])
{
    // Seed random number generator
    srand(time(NULL));

    sput_start_testing();
    sput_enter_suite(__FILE__);

    sput_run_test(test_alloc_game);
    sput_run_test(test_init_game);
    sput_finish_testing();

    free(testState);

    return sput_get_return_value();
}
